 /*******************************************************************************
  * Copyright (c) 2003, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.osgi.framework.internal.reliablefile;

 import java.io.*;
 import java.util.zip.Checksum ;

 /**
  * A ReliableFile FileOutputStream replacement class.
  * This class can be used just like FileOutputStream. The class
  * is in partnership with ReliableFileInputStream to avoid losing
  * file data by using multiple files.
  *
  * @see ReliableFileInputStream
  */
 public class ReliableFileOutputStream extends FilterOutputStream {
     /**
      * ReliableFile object for the file.
      */
     private ReliableFile reliable;

     /**
      * Checksum calculator
      */
     private Checksum crc;

     private boolean outputOpen = false;

     /**
      * Constructs a new ReliableFileOutputStream on the File <code>file</code>. If the
      * file exists, it is written over. See the constructor which can append to
      * the file if so desired.
      *
      * @param file the File on which to stream reads.
      * @exception java.io.IOException If an error occurs opening the file.
      */
     public ReliableFileOutputStream(File file) throws IOException {
         this(ReliableFile.getReliableFile(file), false);
     }

     /**
      * Constructs a new ReliableFileOutputStream on the File <code>file</code>.
      *
      * @param file the File on which to stream reads.
      * @param append a boolean indicating whether or not to append to an existing file.
      * @exception java.io.IOException If an error occurs opening the file.
      */
     public ReliableFileOutputStream(File file, boolean append) throws IOException {
         this(ReliableFile.getReliableFile(file), append);
     }

     /**
      * Constructs a new ReliableFileOutputStream on the file named <code>name</code>. If
      * the file exists, it is written over. See the constructor which can append to
      * the file if so desired.
      * The <code>name</code> may be absolute or relative
      * to the System property <code>"user.dir"</code>.
      *
      * @param name the file on which to stream writes.
      * @exception java.io.IOException If an error occurs opening the file.
      */
     public ReliableFileOutputStream(String name) throws IOException {
         this(ReliableFile.getReliableFile(name), false);
     }

     /**
      * Constructs a new ReliableFileOutputStream on the file named <code>name</code>.
      * The <code>name</code> may be absolute or relative
      * to the System property <code>"user.dir"</code>.
      *
      * @param name the file on which to stream writes.
      * @param append a boolean indicating whether or not to append to an existing file.
      * @exception java.io.IOException If an error occurs opening the file.
      */
     public ReliableFileOutputStream(String name, boolean append) throws IOException {
         this(ReliableFile.getReliableFile(name), append);
     }

     /**
      * Private constructor used by other constructors.
      *
      * @param reliable the ReliableFile on which to read.
      * @param append a boolean indicating whether or not to append to an existing file.
      * @exception java.io.IOException If an error occurs opening the file.
      */
     private ReliableFileOutputStream(ReliableFile reliable, boolean append) throws IOException {
         super(reliable.getOutputStream(append, ReliableFile.GENERATION_LATEST));

         this.reliable = reliable;
         outputOpen = true;
         if (append)
             crc = reliable.getFileChecksum();
         else
             crc = reliable.getChecksumCalculator();
     }

     /**
      * Closes this output stream and releases any system resources
      * associated with this stream. The general contract of <code>close</code>
      * is that it closes the output stream. A closed stream cannot perform
      * output operations and cannot be reopened.
      *
      * @exception java.io.IOException If an error occurs closing the file.
      */
     public synchronized void close() throws IOException {
         closeIntermediateFile();
         reliable.closeOutputFile(crc);
         // if the previouse closeOutpuFile() throws exception,
 // we don't null out reliable to give another opportunity
 // to rename the file.
 reliable = null;
     }

     public File closeIntermediateFile() throws IOException {
         if (reliable == null)
             throw new IOException("ReliableFile stream not open"); //$NON-NLS-1$
 if (outputOpen) {
             // tag on our signature and checksum
 reliable.writeChecksumSignature(out, crc);
             out.flush();
             try {
                 ((FileOutputStream) out).getFD().sync();
             } catch (IOException e) {
                 // just ignore this Exception
 //Debug
 e.printStackTrace();
             }
             out.close();
             outputOpen = false;
         }
         return reliable.getOutputFile();
     }

     /**
      * Override default FilterOutputStream method.
      * @see FilterOutputStream#write(byte[])
      */
     public void write(byte[] b) throws IOException {
         this.write(b, 0, b.length);
     }

     /**
      * Override default FilterOutputStream method.
      * @see FilterOutputStream#write(byte[], int, int)
      */
     public void write(byte[] b, int off, int len) throws IOException {
         out.write(b, off, len);
         crc.update(b, off, len);
     }

     /**
      * Override default FilterOutputStream method.
      * @see FilterOutputStream#write(int)
      */
     public void write(int b) throws IOException {
         out.write(b);
         crc.update((byte) b);
     }

     public void abort() {
         if (reliable == null)
             return;
         if (outputOpen) {
             try {
                 out.close();
             } catch (IOException e) {/*ignore*/
             }
             outputOpen = false;
         }
         reliable.abortOutputFile();
         reliable = null;
     }
 }

